package lexer

import (
	
	
	
	
)

const (
	//XItemError is an error with the parser input
	XItemError XItemType = "Error"
	//XItemAbsLocPath is an absolute path
	XItemAbsLocPath = "Absolute path"
	//XItemAbbrAbsLocPath represents an abbreviated absolute path
	XItemAbbrAbsLocPath = "Abbreviated absolute path"
	//XItemAbbrRelLocPath marks the start of a path expression
	XItemAbbrRelLocPath = "Abbreviated relative path"
	//XItemRelLocPath represents a relative location path
	XItemRelLocPath = "Relative path"
	//XItemEndPath marks the end of a path
	XItemEndPath = "End path instruction"
	//XItemAxis marks an axis specifier of a path
	XItemAxis = "Axis"
	//XItemAbbrAxis marks an abbreviated axis specifier (just @ at this point)
	XItemAbbrAxis = "Abbreviated attribute axis"
	//XItemNCName marks a namespace name in a node test
	XItemNCName = "Namespace"
	//XItemQName marks the local name in an a node test
	XItemQName = "Local name"
	//XItemNodeType marks a node type in a node test
	XItemNodeType = "Node type"
	//XItemProcLit marks a processing-instruction literal
	XItemProcLit = "processing-instruction"
	//XItemFunction marks a function call
	XItemFunction = "function"
	//XItemArgument marks a function argument
	XItemArgument = "function argument"
	//XItemEndFunction marks the end of a function
	XItemEndFunction = "end of function"
	//XItemPredicate marks a predicate in an axis
	XItemPredicate = "predicate"
	//XItemEndPredicate marks a predicate in an axis
	XItemEndPredicate = "end of predicate"
	//XItemStrLit marks a string literal
	XItemStrLit = "string literal"
	//XItemNumLit marks a numeric literal
	XItemNumLit = "numeric literal"
	//XItemOperator marks an operator
	XItemOperator = "operator"
	//XItemVariable marks a variable reference
	XItemVariable = "variable"
)

const (
	eof = -(iota + 1)
)

//XItemType is the parser token types
type XItemType string

//XItem is the token emitted from the parser
type XItem struct {
	Typ XItemType
	Val string
}

type stateFn func(*Lexer) stateFn

//Lexer lexes out XPath expressions
type Lexer struct {
	input string
	start int
	pos   int
	width int
	items chan XItem
}

//Lex an XPath expresion on the io.Reader
func ( string) chan XItem {
	 := &Lexer{
		input: ,
		items: make(chan XItem),
	}
	go .run()
	return .items
}

func ( *Lexer) () {
	for  := startState;  != nil; {
		 = ()
	}

	if .peek() != eof {
		.errorf("Malformed XPath expression")
	}

	close(.items)
}

func ( *Lexer) ( XItemType) {
	.items <- XItem{, .input[.start:.pos]}
	.start = .pos
}

func ( *Lexer) ( XItemType,  string) {
	.items <- XItem{, }
	.start = .pos
}

func ( *Lexer) () ( rune) {
	if .pos >= len(.input) {
		.width = 0
		return eof
	}

	, .width = utf8.DecodeRuneInString(.input[.pos:])

	.pos += .width

	return 
}

func ( *Lexer) () {
	.start = .pos
}

func ( *Lexer) () {
	.pos -= .width
}

func ( *Lexer) () rune {
	 := .next()

	.backup()
	return 
}

func ( *Lexer) ( int) rune {
	if  <= 1 {
		return .peek()
	}

	 := 0
	var  rune

	for  := 0;  < ; ++ {
		,  := utf8.DecodeRuneInString(.input[.pos+:])
		 += 

		if .pos+ > len(.input) {
			return eof
		}

		 = 
	}

	return 
}

func ( *Lexer) ( string) bool {
	if strings.ContainsRune(, .next()) {
		return true
	}

	.backup()
	return false
}

func ( *Lexer) ( string) {
	for strings.ContainsRune(, .next()) {
	}
	.backup()
}

func ( *Lexer) ( int) {
	for  := 0;  < ; ++ {
		.next()
	}
	.ignore()
}

func ( *Lexer) ( bool) {
	for {
		 := .next()

		if  == eof || !unicode.IsSpace() {
			break
		}
	}

	.backup()

	if  {
		.ignore()
	}
}

func ( *Lexer) ( string,  ...interface{}) stateFn {
	.items <- XItem{
		XItemError,
		fmt.Sprintf(, ...),
	}

	return nil
}

func isElemChar( rune) bool {
	return string() != ":" && string() != "/" &&
		(unicode.Is(first, ) || unicode.Is(second, ) || string() == "*") &&
		 != eof
}

func startState( *Lexer) stateFn {
	.skipWS(true)

	if string(.peek()) == "/" {
		.next()
		.ignore()

		if string(.next()) == "/" {
			.ignore()
			return abbrAbsLocPathState
		}

		.backup()
		return absLocPathState
	} else if string(.peek()) == `'` || string(.peek()) == `"` {
		if  := getStrLit(, XItemStrLit);  != nil {
			return .errorf(.Error())
		}

		if .peek() != eof {
			return 
		}
	} else if getNumLit() {
		.skipWS(true)
		if .peek() != eof {
			return 
		}
	} else if string(.peek()) == "$" {
		.next()
		.ignore()
		 := .peek()
		for unicode.Is(first, ) || unicode.Is(second, ) {
			.next()
			 = .peek()
		}
		 := .input[.start:.pos]
		if len() == 0 {
			return .errorf("Empty variable name")
		}
		.emit(XItemVariable)
		.skipWS(true)
		if .peek() != eof {
			return 
		}
	} else if  := findOperatorState();  != nil {
		return 
	} else {
		if isElemChar(.peek()) {
			 := 0

			for {
				if isElemChar(.peek()) {
					.next()
				} else if string(.peek()) == ":" {
					.next()
					++
				} else {
					break
				}
			}

			if string(.peek()) == "(" &&  <= 1 {
				 := .input[.start:.pos]
				 := procFunc(, )
				if  != nil {
					return .errorf(.Error())
				}

				.skipWS(true)

				if string(.peek()) == "/" {
					.next()
					.ignore()

					if string(.next()) == "/" {
						.ignore()
						return abbrRelLocPathState
					}

					.backup()
					return relLocPathState
				}

				return 
			}

			.pos = .start
			return relLocPathState
		} else if string(.peek()) == "@" {
			return relLocPathState
		}
	}

	return nil
}

func strPeek( string,  *Lexer) bool {
	for  := 0;  < len(); ++ {
		if string(.peekAt(+1)) != string([]) {
			return false
		}
	}
	return true
}

func findOperatorState( *Lexer) stateFn {
	.skipWS(true)

	switch string(.peek()) {
	case ">", "<", "!":
		.next()
		if string(.peek()) == "=" {
			.next()
		}
		.emit(XItemOperator)
		return startState
	case "|", "+", "-", "*", "=":
		.next()
		.emit(XItemOperator)
		return startState
	case "(":
		.next()
		.emit(XItemOperator)
		for  := startState;  != nil; {
			 = ()
		}
		.skipWS(true)
		if string(.next()) != ")" {
			return .errorf("Missing end )")
		}
		.emit(XItemOperator)
		return startState
	}

	if strPeek("and", ) {
		.next()
		.next()
		.next()
		.emit(XItemOperator)
		return startState
	}

	if strPeek("or", ) {
		.next()
		.next()
		.emit(XItemOperator)
		return startState
	}

	if strPeek("mod", ) {
		.next()
		.next()
		.next()
		.emit(XItemOperator)
		return startState
	}

	if strPeek("div", ) {
		.next()
		.next()
		.next()
		.emit(XItemOperator)
		return startState
	}

	return nil
}

func getStrLit( *Lexer,  XItemType) error {
	 := .next()
	var  rune

	.ignore()

	for  !=  {
		 = .next()
		if  == eof {
			return fmt.Errorf("Unexpected end of string literal.")
		}
	}

	.backup()
	.emit()
	.next()
	.ignore()

	return nil
}

func getNumLit( *Lexer) bool {
	const  = "0123456789"
	.accept("-")
	 := .pos
	.acceptRun()

	if .pos ==  {
		return false
	}

	if .accept(".") {
		.acceptRun()
	}

	.emit(XItemNumLit)
	return true
}